﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using AutoMapper;
using System.Data.Services.Client;
using System.Reflection;

namespace GDoc.Data.Azure
{

    /// <summary>
    /// Provides generic methods to interact with the azure storage with mapped entities.
    /// </summary>
    /// <typeparam name="T">The type of the domain class.</typeparam>
    /// <typeparam name="S">The type of the azure entity.</typeparam>
    public class RepositoryBase<T, S>
        where T : TableServiceEntity
        where S : class
    {

        /// <summary>
        /// The set to interact with the storage.
        /// </summary>
        public virtual StorageSet<T> Set { get; set; }

        /// <summary>
        /// UnitOfWork to be used in the derived repositiores.
        /// </summary>
        public virtual UnitOfWork UnitOfWork { get; set; }

        /// <summary>
        /// Add an object to the azure context
        /// </summary>
        /// <param name="entity">The entity to add, it will be mapped.</param>
        public virtual void Add(S entity)
        {
            Set.Add(Mapper.Map<S, T>(entity));
            Set.SaveChanges();
        }

        /// <summary>
        /// Deletes an object from the azure context
        /// </summary>
        /// <param name="entity">The entity to delete, it will be mapped.</param>
        public virtual void Remove(S entity)
        {
            try
            {
                var azureEntity = Mapper.Map<S, T>(entity);
                var retrievedEntity = Set.Data.Where(s => s.PartitionKey == azureEntity.PartitionKey && s.RowKey == azureEntity.RowKey).FirstOrDefault();
                if (retrievedEntity != null)
                {
                    Set.Delete(retrievedEntity);
                    Set.SaveChanges();
                }
            }
            catch (DataServiceRequestException)
            {
                return;
            }
            catch (StorageClientException ex)
            {
                if ((int)ex.StatusCode == 404)
                {
                    return;
                }

                throw;
            }
        }

        /// <summary>
        /// Update an object from the azure context
        /// </summary>
        /// <param name="entity">The entity to update, it will be mapped.</param>
        public virtual void Update(S entity)
        {
            try
            {
                var azureEntity = Mapper.Map<S, T>(entity);
                var retrievedEntity = Set.Data.Where(s => s.PartitionKey == azureEntity.PartitionKey && s.RowKey == azureEntity.RowKey).FirstOrDefault();

                retrievedEntity = Mapper.Map<S, T>(entity, retrievedEntity);

                Set.Update(retrievedEntity);
                Set.SaveChanges();
            }
            catch (DataServiceRequestException)
            {
                return;
            }
            catch (StorageClientException ex)
            {
                if ((int)ex.StatusCode == 404)
                {
                    return;
                }

                throw;
            }

        }

    }
}
